﻿using ImageControl.Common;
using ImageControl.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace ImageControl.ViewModels
{
    public class ApplicationViewModel : BindableBase
    {
        private const string GoogleImages = "Resources/GoogleImages.jpg";
        private const string Ninja = "Resources/Ninja.png";

        private readonly AdornerViewModel firstAdorner = null;
        private CoordinateSystemsInfo coordinateInfo = null;
        private ICapturable capturedElement = null;
        private IHoverable hoveredElement = null;

        public ObservableCollection<AdornerViewModel> Adorners { get; private set; }

        public CoordinateSystemsInfo CoordinateInfo
        {
            get
            {
                return coordinateInfo;
            }
            set
            {
                this.SetProperty(ref coordinateInfo, value, "CoordinateInfo");
            }
        }

        public ApplicationViewModel()
        {
            this.Adorners = new ObservableCollection<AdornerViewModel>();
            this.firstAdorner = new AdornerViewModel(GoogleImages, 150, 100, 320, new Point(20, 50));
            this.Adorners.Add(this.firstAdorner);
            this.Adorners.Add(new AdornerViewModel(Ninja, 100, 100, 25, new Point(180, 130)));
            this.capturedElement = null;

            this.RegisterMouseMove(new Point(double.NaN,double.NaN));
        }

        public void RegisterMouseDown(Point documentPoint)
        {
            this.capturedElement = null;
            this.HandleHover(documentPoint);
            if (this.hoveredElement != null)
            {
                this.capturedElement = (ICapturable)this.hoveredElement;
                this.Unhover();
                this.capturedElement.Capture(documentPoint);
            }
        }

        public void RegisterMouseUp(Point documentPoint)
        {
            if (this.capturedElement != null)
            {
                this.capturedElement.ReleaseCapture();
                this.capturedElement = null;
                this.Unhover();
                this.HandleHover(documentPoint);
            }
        }

        public void RegisterMouseMove(Point documentPoint)
        {
            this.RecalculateCoordinateInfo(documentPoint);

            if (this.capturedElement == null)
            {
                HandleHover(documentPoint);
            }
            else
            {
                this.capturedElement.HandleMove(documentPoint);
            }
        }
        
        internal void RegisterMouseLeave(Point point)
        {
            this.Unhover();
        }

        private void RecalculateCoordinateInfo(Point documentPoint)
        {
            AdornerViewModel adorner = null;
            if(this.capturedElement == null)
            {
                adorner = this.firstAdorner;
            }
            else
            {
                adorner = this.capturedElement as AdornerViewModel;

                HandlerViewModel handler = this.capturedElement as HandlerViewModel;
                if (handler != null)
                {
                    adorner = handler.Adorner;
                }
            }

            Point localPoint = adorner.GetLocalCoordinateFromDocumentPoint(documentPoint);
            Point rotatedPoint = adorner.GetRotatedCoordinateFromDocumentPoint(documentPoint);

            this.CoordinateInfo = new CoordinateSystemsInfo(documentPoint, localPoint, rotatedPoint);
        }
  
        private void HandleHover(Point documentPoint)
        {
            IHoverable hovered = null;
            if (this.TryGetHoverable(documentPoint, out hovered))
            {
                if (this.hoveredElement != hovered)
                {
                    this.Unhover();
                    this.hoveredElement = hovered;
                    this.hoveredElement.Hover();
                }
            }
            else
            {
                this.Unhover();
            }
        }

        private void Unhover()
        {
            if(this.hoveredElement != null)
            {
                this.hoveredElement.ReleaseHover();
                this.hoveredElement = null;
            }
        }

        private bool TryGetHoverable(Point documentPoint, out IHoverable hovarable)
        {
            hovarable = null;
            int adornersCount = this.Adorners.Count;

            for (int i = adornersCount - 1; i >= 0; i--)
            {
                AdornerViewModel adorner = this.Adorners[i];
                IHoverable handler = null;
                if (adorner.TryGetHoveredHandler(documentPoint, out handler))
                {
                    hovarable = handler;
                    return true;
                }

                Point rotatedCoordinate = adorner.GetRotatedCoordinateFromDocumentPoint(documentPoint);
                if (adorner.ShouldHandle(rotatedCoordinate))
                {
                    hovarable = adorner;
                    return true;
                }
            }

            return false;
        }
    }
}
